package com.ali.ttt.webSocket.handler;

import com.ali.ttt.core.cocurrent.FutureTaskScheduler;
import com.ali.ttt.netty.common.domain.Message;
import com.ali.ttt.netty.common.listener.MessageListenerProcessor;
import com.ali.ttt.webSocket.domain.ServerSession;
import com.ali.ttt.webSocket.domain.SessionMap;
import com.alibaba.fastjson.JSONObject;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author yuxl
 */
@Slf4j
@Service
@ChannelHandler.Sharable
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    @Resource
    private MessageListenerProcessor messageListenerProcessor;
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        if (frame instanceof PingWebSocketFrame) {
            log.debug("收到ping");
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
        } else if (frame instanceof PongWebSocketFrame) {
            // 刷新连接时长
            log.debug("收到pong");
        } else if (frame instanceof TextWebSocketFrame) {
            String message = ((TextWebSocketFrame) frame).text();
            if("ping".equals(message)) {
                log.debug("收到text ping");
                ctx.channel().writeAndFlush(new TextWebSocketFrame("pong"));
            } else if("pong".equals(message)) {
                // 刷新连接时长
                log.debug("收到text pong");
            } else {
                Message entity = JSONObject.parseObject(message, Message.class);
                FutureTaskScheduler.add(()-> messageListenerProcessor.processMessage(entity,ctx));
            }
        }else{
            String message = "unsupported frame type: " +
                    frame.getClass().getName();
            log.error(message);
            throw new UnsupportedOperationException(message);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        log.debug("channelReadComplete 从客户端收到新的数据、读取完成时调用");
        super.channelReadComplete(ctx);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        log.debug("channelRegistered 注册时执行");
        super.channelRegistered(ctx);
    }

    /**
     * 新连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.debug("收到新连接 channelActive");
        ServerSession session = new ServerSession(ctx.channel());
        session.reverseBind();
        super.channelActive(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.debug("收到新连接 handlerAdded");
        super.handlerAdded(ctx);
    }

    /**
     * 断开连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.debug("断开连接");
        SessionMap.inst().removeSession(ctx.channel().attr(ServerSession.SESSION_KEY).get().getSessionId());
        super.channelInactive(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.debug("移除连接");
        super.handlerRemoved(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.debug("channelUnregistered离线时执行");
        super.channelUnregistered(ctx);
    }

    /**
     * 发生异常
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }
}
